Lazy component
Posted on 2023-04-25 by
henrikvilhelmberglundHere we're making a lazy component that will only load when it is in view. We'll reuse our viewport action that we used a few times before.
By not using import at the top and instead setting a variable to a promise we can load it dynamically when our div with the viewport action comes into view.
<script>
import viewport from "./useViewportAction";
// When using import here, the component will be loaded at the same time as App.svelte which is not what we want.
// import Component from "./Component.svelte";
// This will return a promise that will be resolved when this file is fetched and ready to use
let componentPromise;
let isLoaded = false;
</script>
<div class="h-screen bg-blue-500" />
<div
use:viewport
on:enterViewport={() => {
if (!isLoaded) {
componentPromise = import("./Component.svelte");
}
isLoaded = true;
}} />
{#if componentPromise}
{#await componentPromise}
Loading...
{:then { default: Component }}
<Component foo="123" bar={456} on:click={() => console.log("From App.svelte!")} />
{:catch error}
{error}
{/await}
{/if}
<style>
</style>
This works fine but is a bit annoying because we have to have a lot of code just to load the component when we want to, like having isLoaded
, if statements around our dynamic component and so on.
We can make this better by creating a component for loading a dynamic component .
Bar in App2: 789
<script>
import Lazy from "./Lazy.svelte";
let bar = "789";
</script>
Bar in App2: {bar}
<Lazy this={() => import("./Component.svelte")}>
<div class="text-blue-500" slot="loading">Loading...</div>
<!-- we use a slot to be able to use bind, events like if it was a normal component -->
<svelte:fragment slot="component" let:Component>
<Component foo="123" bind:bar on:click={() => console.log("I am an event in App2.svelte!")} />
</svelte:fragment>
</Lazy>
<style>
</style>
Our dynamically loaded Lazy component is finished and can use bindings and events like if it was a normal component imported statically. Nice!